/*
 *
 */
package org.jiscinvolve.astrodabis.weaver.registry;

import org.junit.Test;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;  

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import java.net.URL;
import java.net.MalformedURLException;

import java.io.FileReader;
import java.io.StringReader;
import java.io.BufferedReader;
import java.io.LineNumberReader;
import java.io.InputStreamReader;
import java.io.IOException;

import java.util.List;
import java.util.ArrayList;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;

import org.jiscinvolve.astrodabis.weaver.TestBase;

/**
 *
 * Find and index TAP services.
 *
 * IVOA Registry of Registries
 * http://rofr.ivoa.net/cgi-bin/listSearchables.py
 *
 * NVO
 * http://nvo.stsci.edu/vor10/advancedsearch.aspx#||ddCapList=table%20service
 *
 * AstroGrid
 * http://registry.astrogrid.org/astrogrid-registry/main/index.jsp
 *
 * MSSL
 * http://msslxv.mssl.ucl.ac.uk/astrogrid-registry/
 *
 * EuroVO
 * http://registry.euro-vo.org/search.jsp
 * http://esavo.esa.int/registry/search.jsp
 *
 *
 */
public class RegQueryTestCase
extends TestBase
    {


    //public static final String endpoint = "http://msslxv.mssl.ucl.ac.uk/astrogrid-registry/xqueryresults.jsp" ;
    public static final String endpoint = "http://registry.astrogrid.org/astrogrid-registry/main/xqueryresults.jsp" ;

    /**
     * Our debug logger.
     * 
     */
    protected static Log log = LogFactory.getLog(RegQueryTestCase.class);

    public void debug(Element element)
        {
        log.debug("TagName [" + element.getTagName() + "]");
        log.debug("Local [" + element.getLocalName() + "]");
        log.debug("Prefix [" + element.getPrefix() + "]");
        log.debug("Namespace [" + element.getNamespaceURI() + "]");
        }

    @Before
    public void before()
        {
        log.debug("RegQueryTestCase.before()");
        }

    @After
    public void after()
        {
        log.debug("RegQueryTestCase.after()");
        }

    public String post(String endpoint, String query)
    throws Exception
        {
       
        //String domain = "http://msslxt.mssl.ucl.ac.uk:8080";
        log.debug("post [" + endpoint + "][" + query + "]");

        long start = System.currentTimeMillis();

        PostMethod post = new PostMethod(
            endpoint
            );
        post.setFollowRedirects(
            false
            ); 
        post.setParameter(
            "performquery",
            "true"
            );
        post.setParameter(
            "ResourceXQuery",
            query
            );

        HttpClient httpclient = new HttpClient();
        httpclient.executeMethod(
            post
            );
        int status = post.getStatusCode();

        log.debug("Status Code [" + status + "]");
        String result = post.getResponseBodyAsString();
        log.debug("Time taken [" + (System.currentTimeMillis() - start) + "]"); 
        post.releaseConnection();

        return result ;
        }
    
    
    public String load(String fileName)
    throws Exception
        {
        BufferedReader in = new BufferedReader(
            new FileReader(
                fileName
                )
            );
        StringBuffer b = new StringBuffer();
        String line = null;
        while((line = in.readLine()) != null)
            {
            b.append(line);
            }
        return b.toString();
        }

    @Test
    public void test000()
    throws Exception
        {

        log.debug(
            post(
                endpoint,
                load(
                    "src/test/data/SimpleQuery.xml"
                    )
                )
            );
        }

    @Test
    public void test001()
    throws Exception
        {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(false);

		DocumentBuilder builder = factory.newDocumentBuilder();

		Document dom = builder.parse(
		    new InputSource(
		        new StringReader(
                    post(
                        endpoint,
                        load(
                            "src/test/data/SimpleQuery.xml"
                            )
                        )
		            )
		        )
		    );

		results(
		    dom.getDocumentElement()
		    );

       }

    public void results(Element element)
        {
        NodeList nodes = element.getElementsByTagName("service");

		for(int i = 0 ; i < nodes.getLength() ; i++)
		    {
		    ServiceMetadata service = new ServiceMetadata(
		        (Element) nodes.item(i)
		        );
            log.debug(service);
			}
        }

    public class ServiceMetadata
        {

        public ServiceMetadata(Element element)
            {

            this.title(element);
            this.ivorn(element);

            this.owner = new Publisher(element);

            this.tap = new Endpoint("ivoa-tap", element);
            this.cap = new Endpoint("vosi-cap", element);
            this.tab = new Endpoint("vosi-tab", element);

            if (this.tab.url() != null)
                {
                this.tables(this.tab.url());
                }
            else if ("base".equals(this.tap.use()))
                {
                if (this.tap.url() != null)
                    {
                    this.tables(this.tap.url() + "/tables");
                    }
                }
            }

        public String toString()
            {
            StringBuffer buffer = new StringBuffer();
            buffer.append("\n");
            buffer.append("Service{");

            buffer.append(" ");
            buffer.append("title{");
            buffer.append(this.title);
            buffer.append("}");

            buffer.append(" ");
            buffer.append("ivorn{");
            buffer.append(this.ivorn);
            buffer.append("}");

            buffer.append("\n");
            buffer.append(this.owner);

            buffer.append("\n");
            buffer.append(this.tap);

            buffer.append("\n");
            buffer.append(this.cap);

            buffer.append("\n");
            buffer.append(this.tab);

            for(Schema schema : schemas)
                {
                buffer.append("\n");
                buffer.append(schema);
                }

            buffer.append("}");
            return buffer.toString();
            }

        private Endpoint tap ;
        public  Endpoint tap()
            {
            return this.tap;
            }

        private Endpoint cap ;
        public  Endpoint cap()
            {
            return this.cap;
            }

        private Endpoint tab ;
        public  Endpoint tab()
            {
            return this.cap;
            }

        private String ivorn ;
        public  String ivorn()
            {
            return this.ivorn;
            }

        private String title ;
        public  String title()
            {
            return this.title;
            }

        private Publisher owner ;
        public  Publisher owner()
            {
            return this.owner;
            }

        protected void title(Element element)
            {
            NodeList outer = element.getElementsByTagName("title");
            if (outer.getLength() > 0)
                {
                NodeList inner = ((Element) outer.item(0)).getElementsByTagName("title");
                if (inner.getLength() > 0)
                    {
                    this.title = inner.item(0).getTextContent();
                    }
                }
            }

        protected void ivorn(Element element)
            {
            NodeList outer = element.getElementsByTagName("ivorn");
            if (outer.getLength() > 0)
                {
                NodeList inner = ((Element) outer.item(0)).getElementsByTagName("identifier");
                if (inner.getLength() > 0)
                    {
                    this.ivorn = inner.item(0).getTextContent();
                    }
                }
            }

        public class Publisher
            {
            public Publisher(Element element)
                {
                NodeList outer = element.getElementsByTagName("owner");
                if (outer.getLength() > 0)
                    {
                    NodeList inner = ((Element) outer.item(0)).getElementsByTagName("publisher");
                    if (inner.getLength() > 0)
                        {
                        NamedNodeMap map = inner.item(0).getAttributes();
                        if (map != null)
                            {
                            Node attrib = map.getNamedItem("ivo-id");
                            if (attrib != null)
                                {
                                this.uri = attrib.getNodeValue();
                                }
                            }
                        this.name = inner.item(0).getTextContent();
                        }
                    }
                }

            private String name ;
            public  String name()
                {
                return this.name ;
                }

            private String uri ;
            public  String uri()
                {
                return this.uri ;
                }

            public String toString()
                {
                StringBuffer buffer = new StringBuffer();
                buffer.append("Publisher{");

                buffer.append(" ");
                buffer.append("name{");
                buffer.append(this.name);
                buffer.append("}");

                buffer.append(" ");
                buffer.append("uri{");
                buffer.append(this.uri);
                buffer.append("}");

                buffer.append("}");
                return buffer.toString();
                }

            }

        public class Endpoint
            {

            public Endpoint(String type, Element element)
                {
                this.type = type ;

                NodeList outer = element.getElementsByTagName(type);
                if (outer.getLength() > 0)
                    {
                    NodeList inner = ((Element) outer.item(0)).getElementsByTagName("accessURL");
                    if (inner.getLength() > 0)
                        {
                        NamedNodeMap map = inner.item(0).getAttributes();
                        if (map != null)
                            {
                            Node attrib = map.getNamedItem("use");
                            if (attrib != null)
                                {
                                this.use = attrib.getNodeValue();
                                }
                            }
                        this.url = inner.item(0).getTextContent();
                        }
                    }
                }

            private String type ;
            public String type()
                {
                return this.type ;
                }

            private String use ;
            public String use()
                {
                return this.use ;
                }

            private String url ;
            public String url()
                {
                return this.url ;
                }

            public String toString()
                {
                StringBuffer buffer = new StringBuffer();
                buffer.append("Endpoint{");

                buffer.append(" ");
                buffer.append("type{");
                buffer.append(this.type);
                buffer.append("}");

                buffer.append(" ");
                buffer.append("use{");
                buffer.append(this.use);
                buffer.append("}");

                buffer.append(" ");
                buffer.append("url{");
                buffer.append(this.url);
                buffer.append("}");

                buffer.append("}");
                return buffer.toString();
                }

            }

        public void tables(String url)
            {
            try {
                this.tables(
                    new URL(
                        url
                        )
                    );
                }
            catch (MalformedURLException ouch)
                {
                log.error("URL error [" + url + "]");
                }
            }

        public void tables(URL url)
            {
            try {

                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                factory.setNamespaceAware(true);

		        DocumentBuilder builder = factory.newDocumentBuilder();

		        Document dom = builder.parse(
		            new InputSource(
                        new InputStreamReader(
                            url.openStream()
                            )
                        )
                    );

    		    this.schemas(
    		        dom.getDocumentElement()
    		        );

                }
            catch (IOException ouch)
                {
                log.error("Error reading tables [" + ouch.getMessage() + "][" + ouch.getClass() + "]");
                }
            catch (SAXException ouch)
                {
                log.error("Error reading tables [" + ouch.getMessage() + "][" + ouch.getClass() + "]");
                }
            catch (ParserConfigurationException ouch)
                {
                log.error("Error reading tables [" + ouch.getMessage() + "][" + ouch.getClass() + "]");
                }

            }

        public void schemas(Element element)
            {
            NodeList nodes = element.getElementsByTagName("schema");
            if (nodes.getLength() > 0)
                {
                for(int i = 0 ; i < nodes.getLength() ; i++)
                    {
                    schemas.add(
                        new Schema(
                            (Element) nodes.item(i)
                            )
                        );
                    }
                }
            else if ("tables".equals(element.getLocalName()))
                {
                schemas.add(
                    new Schema(
                        "default",
                        element
                        )
                    );
                }
            }

        private List<Schema> schemas = new ArrayList<Schema>();
        public  Iterable<Schema> schemas()
            {
            return this.schemas ;
            }

        public class Schema
            {

            public Schema(String name, Element element)
                {
                this.name(name) ;
                this.tables(element);
                }

            public Schema(Element element)
                {
                this.name(element);
                this.tables(element);
                }

            private String name ;
            public  String name()
                {
                return this.name ;
                }

            private void name(String name)
                {
                this.name = name ;
                }

            private void name(Element element)
                {
                NodeList nodes = element.getElementsByTagName("name");
                if (nodes.getLength() > 0)
                    {
                    this.name = nodes.item(0).getTextContent();
                    }
                }

            private void tables(Element element)
                {
                NodeList nodes = element.getElementsByTagName("table");
                for(int i = 0 ; i < nodes.getLength() ; i++)
                    {
                    this.tables.add(
                        new Table(
                            (Element) nodes.item(i)
                            )
                        );
                    }
                }

            public String toString()
                {
                StringBuffer buffer = new StringBuffer();
                buffer.append("Schema{");

                buffer.append(" ");
                buffer.append("name{");
                buffer.append(this.name);
                buffer.append("}");

                for(Table table : tables)
                    {
                    buffer.append("\n");
                    buffer.append(table);
                    }

                buffer.append("}");
                return buffer.toString();            
                }

            private List<Table> tables = new ArrayList<Table>();
            public  Iterable<Table> tables()
                {
                return this.tables ;
                }

            public class Table
                {
                public Table(Element element)
                    {
                    this.name(element);
                    this.columns(element);
                    }

                private String name ;
                public  String name()
                    {
                    return this.name ;
                    }

                private void name(Element element)
                    {
                    NodeList nodes = element.getElementsByTagName("name");
                    if (nodes.getLength() > 0)
                        {
                        this.name = nodes.item(0).getTextContent();
                        }
                    }

                public String toString()
                    {
                    StringBuffer buffer = new StringBuffer();
                    buffer.append("Table{");

                    buffer.append(" ");
                    buffer.append("name{");
                    buffer.append(this.name);
                    buffer.append("}");

                    for(Column column : columns)
                        {
                        buffer.append("\n");
                        buffer.append(column);
                        }

                    buffer.append("}");
                    return buffer.toString();            
                    }

                private void columns(Element element)
                    {
                    NodeList nodes = element.getElementsByTagName("column");
                    for(int i = 0 ; i < nodes.getLength() ; i++)
                        {
                        this.columns.add(
                            new Column(
                                (Element) nodes.item(i)
                                )
                            );
                        }
                    }

                private List<Column> columns = new ArrayList<Column>();
                public  Iterable<Column> columns()
                    {
                    return this.columns ;
                    }

                public class Column
                    {
                    public Column(Element element)
                        {
                        this.name(element);
                        }

                    private String name ;
                    public  String name()
                        {
                        return this.name ;
                        }

                    private void name(Element element)
                        {
                        NodeList nodes = element.getElementsByTagName("name");
                        if (nodes.getLength() > 0)
                            {
                            this.name = nodes.item(0).getTextContent();
                            }
                        }

                    public String toString()
                        {
                        StringBuffer buffer = new StringBuffer();
                        buffer.append("Column{");

                        buffer.append(" ");
                        buffer.append("name{");
                        buffer.append(this.name);
                        buffer.append("}");

                        buffer.append("}");
                        return buffer.toString();            
                        }
                    }
                }
            }
        }
    }

